home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / chrome / toolkit.jar / content / global / customizeToolbar.js < prev    next >
Encoding:
Text File  |  2005-03-04  |  28.3 KB  |  963 lines

  1. //@line 40 "/c/mozilla/toolkit/content/customizeToolbar.js"
  2.  
  3. const kRowMax = 4;
  4. const kWindowWidth = 635;
  5. const kWindowHeight = 400;
  6. const kAnimateIncrement = 50;
  7. const kAnimateSteps = kWindowHeight / kAnimateIncrement - 1;
  8. const kVSizeSlop = 5;
  9.  
  10. var gToolboxDocument = null;
  11. var gToolbox = null;
  12. var gCurrentDragOverItem = null;
  13. var gToolboxChanged = false;
  14. var gToolboxIconSize = false;
  15.  
  16. function onLoad()
  17. {
  18.   gToolbox = window.arguments[0];
  19.   gToolboxDocument = gToolbox.ownerDocument;
  20.   
  21.   gToolbox.addEventListener("draggesture", onToolbarDragGesture, false);
  22.   gToolbox.addEventListener("dragover", onToolbarDragOver, false);
  23.   gToolbox.addEventListener("dragexit", onToolbarDragExit, false);
  24.   gToolbox.addEventListener("dragdrop", onToolbarDragDrop, false);
  25.  
  26.   repositionDialog();
  27.   
  28.   initDialog();
  29. }
  30.  
  31. function onUnload(aEvent)
  32. {
  33.   removeToolboxListeners();
  34.   unwrapToolbarItems();
  35.   persistCurrentSets();
  36.   
  37.   notifyParentComplete();
  38.   
  39.   window.close();
  40. }
  41.  
  42. function onAccept(aEvent)
  43. {
  44.   document.getElementById("main-box").collapsed = true;
  45.   window.close();
  46. }
  47.  
  48. function initDialog()
  49. {
  50.   document.getElementById("main-box").collapsed = false;
  51.   
  52.   var mode = gToolbox.getAttribute("mode");
  53.   document.getElementById("modelist").value = mode;
  54.   gToolboxIconSize = gToolbox.getAttribute("iconsize");
  55.   var smallIconsCheckbox = document.getElementById("smallicons");
  56.   if (mode == "text")
  57.     smallIconsCheckbox.disabled = true;
  58.   else
  59.     smallIconsCheckbox.checked = gToolboxIconSize == "small"; 
  60.  
  61.   // Build up the palette of other items.
  62.   buildPalette();
  63.  
  64.   // Wrap all the items on the toolbar in toolbarpaletteitems.
  65.   wrapToolbarItems();
  66. }
  67.  
  68. function repositionDialog()
  69. {
  70.   // Position the dialog touching the bottom of the toolbox and centered with 
  71.   // it. We must resize the window smaller first so that it is positioned 
  72.   // properly. 
  73.   var screenX = gToolbox.boxObject.screenX + ((gToolbox.boxObject.width - kWindowWidth) / 2);
  74.   var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
  75.  
  76.   var newHeight = kWindowHeight;
  77.   if (newHeight >= screen.availHeight - screenY - kVSizeSlop) {
  78.     newHeight = screen.availHeight - screenY - kVSizeSlop;
  79.   }
  80.  
  81.   window.resizeTo(kWindowWidth, newHeight);
  82.   window.moveTo(screenX, screenY);
  83. }
  84.  
  85. function removeToolboxListeners()
  86. {
  87.   gToolbox.removeEventListener("draggesture", onToolbarDragGesture, false);
  88.   gToolbox.removeEventListener("dragover", onToolbarDragOver, false);
  89.   gToolbox.removeEventListener("dragexit", onToolbarDragExit, false);
  90.   gToolbox.removeEventListener("dragdrop", onToolbarDragDrop, false);
  91. }
  92.  
  93. /**
  94.  * Invoke a callback on the toolbox to notify it that the dialog is done
  95.  * and going away.
  96.  */
  97. function notifyParentComplete()
  98. {
  99.   if ("customizeDone" in gToolbox)
  100.     gToolbox.customizeDone(gToolboxChanged);
  101. }
  102.  
  103. function getToolbarAt(i)
  104. {
  105.   return gToolbox.childNodes[i];
  106. }
  107.  
  108. /**
  109.  * Persist the current set of buttons in all customizable toolbars to
  110.  * localstore.
  111.  */
  112. function persistCurrentSets()
  113. {
  114.   if (!gToolboxChanged)
  115.     return;
  116.  
  117.   var customCount = 0;
  118.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  119.     // Look for customizable toolbars that need to be persisted.
  120.     var toolbar = getToolbarAt(i);
  121.     if (isCustomizableToolbar(toolbar)) {
  122.       // Calculate currentset and store it in the attribute.
  123.       var currentSet = toolbar.currentSet;
  124.       toolbar.setAttribute("currentset", currentSet);
  125.       
  126.       var customIndex = toolbar.hasAttribute("customindex");
  127.       if (customIndex) {
  128.         if (!toolbar.firstChild) {
  129.           // Remove custom toolbars whose contents have been removed.
  130.           gToolbox.removeChild(toolbar);
  131.           --i;
  132.         } else {
  133.           // Persist custom toolbar info on the <toolbarset/>
  134.           gToolbox.toolbarset.setAttribute("toolbar"+(++customCount),
  135.                                            toolbar.toolbarName + ":" + currentSet);
  136.           gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
  137.         }
  138.       }
  139.  
  140.       if (!customIndex) {
  141.         // Persist the currentset attribute directly on hardcoded toolbars.
  142.         gToolboxDocument.persist(toolbar.id, "currentset");
  143.       }
  144.     }
  145.   }
  146.   
  147.   // Remove toolbarX attributes for removed toolbars.
  148.   while (gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
  149.     gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
  150.     gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
  151.   }
  152. }
  153.  
  154. /**
  155.  * Wraps all items in all customizable toolbars in a toolbox.
  156.  */
  157. function wrapToolbarItems()
  158. {
  159.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  160.     var toolbar = getToolbarAt(i);
  161.     if (isCustomizableToolbar(toolbar)) {
  162.       for (var k = 0; k < toolbar.childNodes.length; ++k) {
  163.         var item = toolbar.childNodes[k];
  164.         if (isToolbarItem(item)) {
  165.           var nextSibling = item.nextSibling;
  166.           
  167.           var wrapper = wrapToolbarItem(item);
  168.           
  169.           if (nextSibling)
  170.             toolbar.insertBefore(wrapper, nextSibling);
  171.           else
  172.             toolbar.appendChild(wrapper);
  173.         }
  174.       }
  175.     }
  176.   }
  177. }
  178.  
  179. /**
  180.  * Unwraps all items in all customizable toolbars in a toolbox.
  181.  */
  182. function unwrapToolbarItems()
  183. {
  184.   var paletteItems = gToolbox.getElementsByTagName("toolbarpaletteitem");
  185.   var paletteItem;
  186.   while ((paletteItem = paletteItems.item(0)) != null) {
  187.     var toolbarItem = paletteItem.firstChild;
  188.  
  189.     if (paletteItem.hasAttribute("itemdisabled"))
  190.       toolbarItem.disabled = true;
  191.  
  192.     if (paletteItem.hasAttribute("itemcommand"))
  193.       toolbarItem.setAttribute("command", paletteItem.getAttribute("itemcommand"));
  194.  
  195.     // We need the removeChild here because replaceChild and XBL no workee
  196.     // together.  See bug 193298.
  197.     paletteItem.removeChild(toolbarItem);
  198.     paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
  199.   }
  200. }
  201.  
  202. /**
  203.  * Creates a wrapper that can be used to contain a toolbaritem and prevent
  204.  * it from receiving UI events.
  205.  */
  206. function createWrapper(aId)
  207. {
  208.   var wrapper = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  209.                                          "toolbarpaletteitem");
  210.  
  211.   wrapper.id = "wrapper-"+aId;  
  212.   return wrapper;
  213. }
  214.  
  215. /**
  216.  * Wraps an item that has been cloned from a template and adds
  217.  * it to the end of a row in the palette.
  218.  */
  219. function wrapPaletteItem(aPaletteItem, aCurrentRow, aSpacer)
  220. {
  221.   var wrapper = createWrapper(aPaletteItem.id);
  222.  
  223.   wrapper.setAttribute("flex", 1);
  224.   wrapper.setAttribute("align", "center");
  225.   wrapper.setAttribute("pack", "center");
  226.   wrapper.setAttribute("minheight", "0");
  227.   wrapper.setAttribute("minwidth", "0");
  228.  
  229.   wrapper.appendChild(aPaletteItem);
  230.   
  231.   // XXX We need to call this AFTER the palette item has been appended
  232.   // to the wrapper or else we crash dropping certain buttons on the 
  233.   // palette due to removal of the command and disabled attributes - JRH
  234.   cleanUpItemForPalette(aPaletteItem, wrapper);
  235.  
  236.   if (aSpacer)
  237.     aCurrentRow.insertBefore(wrapper, aSpacer);
  238.   else
  239.     aCurrentRow.appendChild(wrapper);
  240.  
  241. }
  242.  
  243. /**
  244.  * Wraps an item that is currently on a toolbar and replaces the item
  245.  * with the wrapper. This is not used when dropping items from the palette,
  246.  * only when first starting the dialog and wrapping everything on the toolbars.
  247.  */
  248. function wrapToolbarItem(aToolbarItem)
  249. {
  250.   var wrapper = createWrapper(aToolbarItem.id);
  251.   
  252.   cleanupItemForToolbar(aToolbarItem, wrapper);
  253.   wrapper.flex = aToolbarItem.flex;
  254.  
  255.   if (aToolbarItem.parentNode)
  256.     aToolbarItem.parentNode.removeChild(aToolbarItem);
  257.   
  258.   wrapper.appendChild(aToolbarItem);
  259.   
  260.   return wrapper;
  261. }
  262.  
  263. /**
  264.  * Get the list of ids for the current set of items on each toolbar.
  265.  */
  266. function getCurrentItemIds()
  267. {
  268.   var currentItems = {};
  269.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  270.     var toolbar = getToolbarAt(i);
  271.     if (isCustomizableToolbar(toolbar)) {
  272.       var child = toolbar.firstChild;
  273.       while (child) {
  274.         if (isToolbarItem(child))
  275.           currentItems[child.id] = 1;
  276.         child = child.nextSibling;
  277.       }
  278.     }
  279.   }
  280.   return currentItems;
  281. }
  282.  
  283. /**
  284.  * Builds the palette of draggable items that are not yet in a toolbar.
  285.  */
  286. function buildPalette()
  287. {
  288.   // Empty the palette first.
  289.   var paletteBox = document.getElementById("palette-box");
  290.   while (paletteBox.lastChild)
  291.     paletteBox.removeChild(paletteBox.lastChild);
  292.  
  293.   var currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  294.                                             "hbox");
  295.   currentRow.setAttribute("class", "paletteRow");
  296.  
  297.   // Add the toolbar separator item.
  298.   var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  299.                                               "toolbarseparator");
  300.   templateNode.id = "separator";
  301.   wrapPaletteItem(templateNode, currentRow, null);
  302.  
  303.   // Add the toolbar spring item.
  304.   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  305.                                               "toolbarspring");
  306.   templateNode.id = "spring";
  307.   templateNode.flex = 1;
  308.   wrapPaletteItem(templateNode, currentRow, null);
  309.  
  310.   // Add the toolbar spacer item.
  311.   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  312.                                               "toolbarspacer");
  313.   templateNode.id = "spacer";
  314.   templateNode.flex = 1;
  315.   wrapPaletteItem(templateNode, currentRow, null);
  316.  
  317.   var rowSlot = 3;
  318.  
  319.   var currentItems = getCurrentItemIds();
  320.   templateNode = gToolbox.palette.firstChild;
  321.   while (templateNode) {
  322.     // Check if the item is already in a toolbar before adding it to the palette.
  323.     if (!(templateNode.id in currentItems)) {
  324.       var paletteItem = templateNode.cloneNode(true);
  325.  
  326.       if (rowSlot == kRowMax) {
  327.         // Append the old row.
  328.         paletteBox.appendChild(currentRow);
  329.  
  330.         // Make a new row.
  331.         currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  332.                                               "hbox");
  333.         currentRow.setAttribute("class", "paletteRow");
  334.         rowSlot = 0;
  335.       }
  336.  
  337.       ++rowSlot;
  338.       wrapPaletteItem(paletteItem, currentRow, null);
  339.     }
  340.     
  341.     templateNode = templateNode.nextSibling;
  342.   }
  343.  
  344.   if (currentRow) { 
  345.     fillRowWithFlex(currentRow);
  346.     paletteBox.appendChild(currentRow);
  347.   }
  348. }
  349.  
  350. /**
  351.  * Creates a new palette item for a cloned template node and
  352.  * adds it to the last slot in the palette.
  353.  */
  354. function appendPaletteItem(aItem)
  355. {
  356.   var paletteBox = document.getElementById("palette-box");
  357.   var lastRow = paletteBox.lastChild;
  358.   var lastSpacer = lastRow.lastChild;
  359.    
  360.   if (lastSpacer.localName != "spacer") {
  361.     // The current row is full, so we have to create a new row.
  362.     lastRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  363.                                         "hbox");
  364.     lastRow.setAttribute("class", "paletteRow");
  365.     paletteBox.appendChild(lastRow);
  366.     
  367.     wrapPaletteItem(aItem, lastRow, null);
  368.  
  369.     fillRowWithFlex(lastRow);
  370.   } else {
  371.     // Decrement the flex of the last spacer or remove it entirely.
  372.     var flex = lastSpacer.getAttribute("flex");
  373.     if (flex == 1) {
  374.       lastRow.removeChild(lastSpacer);
  375.       lastSpacer = null;
  376.     } else
  377.       lastSpacer.setAttribute("flex", --flex);
  378.  
  379.     // Insert the wrapper where the last spacer was.
  380.     wrapPaletteItem(aItem, lastRow, lastSpacer);
  381.   }
  382. }
  383.  
  384. function fillRowWithFlex(aRow)
  385. {
  386.   var remainingFlex = kRowMax - aRow.childNodes.length;
  387.   if (remainingFlex > 0) {
  388.     var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  389.                                           "spacer");
  390.     spacer.setAttribute("flex", remainingFlex);
  391.     aRow.appendChild(spacer);
  392.   }
  393. }
  394.  
  395. /**
  396.  * Makes sure that an item that has been cloned from a template
  397.  * is stripped of all properties that may adversely affect it's
  398.  * appearance in the palette.
  399.  */
  400. function cleanUpItemForPalette(aItem, aWrapper)
  401. {
  402.   aWrapper.setAttribute("place", "palette");
  403.   setWrapperType(aItem, aWrapper);
  404.  
  405.   if (aItem.hasAttribute("title"))
  406.     aWrapper.setAttribute("title", aItem.getAttribute("title"));
  407.   else if (isSpecialItem(aItem)) {
  408.     var stringBundle = document.getElementById("stringBundle");
  409.     var title = stringBundle.getString(aItem.id + "Title");
  410.     aWrapper.setAttribute("title", title);
  411.   }
  412.   
  413.   // Remove attributes that screw up our appearance.
  414.   aItem.removeAttribute("command");
  415.   aItem.removeAttribute("observes");
  416.   aItem.removeAttribute("disabled");
  417.   aItem.removeAttribute("type");
  418.   
  419.   if (aItem.localName == "toolbaritem" && aItem.firstChild) {
  420.     aItem.firstChild.removeAttribute("observes");
  421.  
  422.     // So the throbber doesn't throb in the dialog,
  423.     // cute as that may be...
  424.     aItem.firstChild.removeAttribute("busy");
  425.   }
  426. }
  427.  
  428. /**
  429.  * Makes sure that an item that has been cloned from a template
  430.  * is stripped of all properties that may adversely affect it's
  431.  * appearance in the toolbar.  Store critical properties on the 
  432.  * wrapper so they can be put back on the item when we're done.
  433.  */
  434. function cleanupItemForToolbar(aItem, aWrapper)
  435. {
  436.   setWrapperType(aItem, aWrapper);
  437.   aWrapper.setAttribute("place", "toolbar");
  438.  
  439.   if (aItem.hasAttribute("command")) {
  440.     aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
  441.     aItem.removeAttribute("command");
  442.   }
  443.  
  444.   if (aItem.disabled) {
  445.     aWrapper.setAttribute("itemdisabled", "true");
  446.     aItem.disabled = false;
  447.   }
  448. }
  449.  
  450. function setWrapperType(aItem, aWrapper)
  451. {
  452.   if (aItem.localName == "toolbarseparator") {
  453.     aWrapper.setAttribute("type", "separator");
  454.   } else if (aItem.localName == "toolbarspring") {
  455.     aWrapper.setAttribute("type", "spring");
  456.   } else if (aItem.localName == "toolbarspacer") {
  457.     aWrapper.setAttribute("type", "spacer");
  458.   } else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
  459.     aWrapper.setAttribute("type", aItem.firstChild.localName);
  460.   }
  461. }
  462.  
  463. function setDragActive(aItem, aValue)
  464. {
  465.   var node = aItem;
  466.   var direction = window.getComputedStyle(aItem, null).direction;
  467.   var value = direction == "ltr"? "left" : "right";
  468.   if (aItem.localName == "toolbar") {
  469.     node = aItem.lastChild;
  470.     value = direction == "ltr"? "right" : "left";
  471.   }
  472.   
  473.   if (!node)
  474.     return;
  475.   
  476.   if (aValue) {
  477.     if (!node.hasAttribute("dragover"))
  478.       node.setAttribute("dragover", value);
  479.   } else {
  480.     node.removeAttribute("dragover");
  481.   }
  482. }
  483.  
  484. function addNewToolbar()
  485. {
  486.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  487.                                 .getService(Components.interfaces.nsIPromptService);
  488.  
  489.   var stringBundle = document.getElementById("stringBundle");
  490.   var message = stringBundle.getString("enterToolbarName");
  491.   var title = stringBundle.getString("enterToolbarTitle");
  492.  
  493.   var name = {};
  494.  
  495.   while (true) {
  496.  
  497.     if (!promptService.prompt(window, title, message, name, null, {}))
  498.       return;
  499.     
  500.     if (!name.value) {
  501.       message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]);
  502.       continue;
  503.     }
  504.  
  505.     var dupeFound = false;
  506.  
  507.      // Check for an existing toolbar with the same display name
  508.     for (i = 0; i < gToolbox.childNodes.length; ++i) {
  509.       var toolbar = gToolbox.childNodes[i];
  510.       var toolbarName = toolbar.getAttribute("toolbarname");
  511.  
  512.       if (toolbarName == name.value &&
  513.           toolbar.getAttribute("type") != "menubar" &&
  514.           toolbar.nodeName == 'toolbar') {
  515.         dupeFound = true;
  516.         break;
  517.       }
  518.     }
  519.  
  520.     if (!dupeFound)
  521.       break;
  522.  
  523.     message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
  524.   }
  525.     
  526.   gToolbox.appendCustomToolbar(name.value, "");
  527.   
  528.   repositionDialog();
  529.   gToolboxChanged = true;
  530. }
  531.  
  532. /**
  533.  * Restore the default set of buttons to fixed toolbars,
  534.  * remove all custom toolbars, and rebuild the palette.
  535.  */
  536. function restoreDefaultSet()
  537. {
  538.   // Save disabled/command states, because we're
  539.   // going to recreate the wrappers and lose this
  540.   var savedAttributes = saveItemAttributes(["itemdisabled", "itemcommand"]);
  541.  
  542.   // Restore the defaultset for fixed toolbars.
  543.   var toolbar = gToolbox.firstChild;
  544.   while (toolbar) {
  545.     if (isCustomizableToolbar(toolbar)) {
  546.       if (!toolbar.hasAttribute("customindex")) {
  547.         var defaultSet = toolbar.getAttribute("defaultset");
  548.         if (defaultSet)
  549.           toolbar.currentSet = defaultSet;
  550.       }
  551.     }
  552.     toolbar = toolbar.nextSibling;
  553.   }
  554.  
  555.   // Restore the default icon size (large) and mode (icons only).
  556.   updateIconSize(false);
  557.   document.getElementById("smallicons").checked = false;
  558.   updateToolbarMode("icons");
  559.   document.getElementById("modelist").value = "icons";
  560.   
  561.   // Remove all of the customized toolbars.
  562.   var child = gToolbox.lastChild;
  563.   while (child) {
  564.     if (child.hasAttribute("customindex")) {
  565.       var thisChild = child;
  566.       child = child.previousSibling;
  567.       gToolbox.removeChild(thisChild);
  568.     } else {
  569.       child = child.previousSibling;
  570.     }
  571.   }
  572.   
  573.   // Now rebuild the palette.
  574.   buildPalette();
  575.  
  576.   // Now re-wrap the items on the toolbar.
  577.   wrapToolbarItems();
  578.  
  579.   // Restore the disabled and command states
  580.   restoreItemAttributes(["itemdisabled", "itemcommand"], savedAttributes);
  581.  
  582.   repositionDialog();
  583.   gToolboxChanged = true;
  584. }
  585.  
  586. function saveItemAttributes(aAttributeList)
  587. {
  588.   var items = [];
  589.   var paletteItems = gToolbox.getElementsByTagName("toolbarpaletteitem");
  590.   for (var i = 0; i < paletteItems.length; i++) {
  591.     var paletteItem = paletteItems.item(i);
  592.     for (var j = 0; j < aAttributeList.length; j++) {
  593.       var attr = aAttributeList[j];
  594.       if (paletteItem.hasAttribute(attr)) {
  595.         items.push([paletteItem.id, attr, paletteItem.getAttribute(attr)]);
  596.       }
  597.     }
  598.   }
  599.   return items;
  600. }
  601.  
  602. function restoreItemAttributes(aAttributeList, aSavedAttrList)
  603. {
  604.   var paletteItems = gToolbox.getElementsByTagName("toolbarpaletteitem");
  605.  
  606.   for (var i = 0; i < paletteItems.length; i++) {
  607.     var paletteItem = paletteItems.item(i);
  608.  
  609.     // if the item is supposed to have this, it'll get
  610.     // restored from the saved list
  611.     for (var j = 0; j < aAttributeList.length; j++)
  612.       paletteItem.removeAttribute(aAttributeList[j]);
  613.  
  614.     for (var j = 0; j < aSavedAttrList.length; j++) {
  615.       var savedAttr = aSavedAttrList[j];
  616.       if (paletteItem.id == savedAttr[0]) {
  617.         paletteItem.setAttribute(savedAttr[1], savedAttr[2]);
  618.       }
  619.     }
  620.   }
  621. }
  622.  
  623. function updateIconSize(aUseSmallIcons)
  624. {
  625.   gToolboxIconSize = aUseSmallIcons ? "small" : "large";
  626.   
  627.   setAttribute(gToolbox, "iconsize", gToolboxIconSize);
  628.   gToolboxDocument.persist(gToolbox.id, "iconsize");
  629.   
  630.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  631.     var toolbar = getToolbarAt(i);
  632.     if (isCustomizableToolbar(toolbar)) {
  633.       setAttribute(toolbar, "iconsize", gToolboxIconSize);
  634.       gToolboxDocument.persist(toolbar.id, "iconsize");
  635.     }
  636.   }
  637.  
  638.   repositionDialog();
  639. }
  640.  
  641. function updateToolbarMode(aModeValue)
  642. {
  643.   setAttribute(gToolbox, "mode", aModeValue);
  644.   gToolboxDocument.persist(gToolbox.id, "mode");
  645.  
  646.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  647.     var toolbar = getToolbarAt(i);
  648.     if (isCustomizableToolbar(toolbar)) {
  649.       setAttribute(toolbar, "mode", aModeValue);
  650.       gToolboxDocument.persist(toolbar.id, "mode");
  651.     }
  652.   }
  653.  
  654.   var iconSizeCheckbox = document.getElementById("smallicons");
  655.   iconSizeCheckbox.disabled = aModeValue == "text";
  656.  
  657.   repositionDialog();
  658. }
  659.  
  660.  
  661. function setAttribute(aElt, aAttr, aVal)
  662. {
  663.  if (aVal)
  664.     aElt.setAttribute(aAttr, aVal);
  665.   else
  666.     aElt.removeAttribute(aAttr);
  667. }
  668.  
  669. function isCustomizableToolbar(aElt)
  670. {
  671.   return aElt.localName == "toolbar" &&
  672.          aElt.getAttribute("customizable") == "true";
  673. }
  674.  
  675. function isSpecialItem(aElt)
  676. {
  677.   return aElt.localName == "toolbarseparator" ||
  678.          aElt.localName == "toolbarspring" ||
  679.          aElt.localName == "toolbarspacer";
  680. }
  681.  
  682. function isToolbarItem(aElt)
  683. {
  684.   return aElt.localName == "toolbarbutton" ||
  685.          aElt.localName == "toolbaritem" ||
  686.          aElt.localName == "toolbarseparator" ||
  687.          aElt.localName == "toolbarspring" ||
  688.          aElt.localName == "toolbarspacer";
  689. }
  690.  
  691. ///////////////////////////////////////////////////////////////////////////
  692. //// Drag and Drop observers
  693.  
  694. function onToolbarDragGesture(aEvent)
  695. {
  696.   nsDragAndDrop.startDrag(aEvent, dragStartObserver);
  697. }
  698.  
  699. function onToolbarDragOver(aEvent)
  700. {
  701.   nsDragAndDrop.dragOver(aEvent, toolbarDNDObserver);
  702. }
  703.  
  704. function onToolbarDragDrop(aEvent)
  705. {
  706.   nsDragAndDrop.drop(aEvent, toolbarDNDObserver);
  707. }
  708.  
  709. function onToolbarDragExit(aEvent)
  710. {
  711.   if (gCurrentDragOverItem)
  712.     setDragActive(gCurrentDragOverItem, false);
  713. }
  714.  
  715. var dragStartObserver =
  716. {
  717.   onDragStart: function (aEvent, aXferData, aDragAction) {
  718.     var documentId = gToolboxDocument.documentElement.id;
  719.     
  720.     var item = aEvent.target;
  721.     while (item && item.localName != "toolbarpaletteitem")
  722.       item = item.parentNode;
  723.     
  724.     item.setAttribute("dragactive", "true");
  725.     
  726.     aXferData.data = new TransferDataSet();
  727.     var data = new TransferData();
  728.     data.addDataForFlavour("text/toolbarwrapper-id/"+documentId, item.firstChild.id);
  729.     aXferData.data.push(data);
  730.     aDragAction.action = Components.interfaces.nsIDragService.DRAGDROP_ACTION_MOVE;
  731.   }
  732. }
  733.  
  734. var toolbarDNDObserver =
  735. {
  736.   onDragOver: function (aEvent, aFlavour, aDragSession)
  737.   {
  738.     var toolbar = aEvent.target;
  739.     var dropTarget = aEvent.target;
  740.     while (toolbar && toolbar.localName != "toolbar") {
  741.       dropTarget = toolbar;
  742.       toolbar = toolbar.parentNode;
  743.     }
  744.     
  745.     var previousDragItem = gCurrentDragOverItem;
  746.  
  747.     // Make sure we are dragging over a customizable toolbar.
  748.     if (!isCustomizableToolbar(toolbar)) {
  749.       gCurrentDragOverItem = null;
  750.       return;
  751.     }
  752.     
  753.     if (dropTarget.localName == "toolbar") {
  754.       gCurrentDragOverItem = dropTarget;
  755.     } else {
  756.       gCurrentDragOverItem = null;
  757.  
  758.       var direction = window.getComputedStyle(dropTarget.parentNode, null).direction;
  759.       var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2);
  760.       if (direction == "ltr")
  761.         dragAfter = aEvent.clientX > dropTargetCenter;
  762.       else
  763.         dragAfter = aEvent.clientX < dropTargetCenter;
  764.         
  765.       if (dragAfter) {
  766.         gCurrentDragOverItem = dropTarget.nextSibling;
  767.         if (!gCurrentDragOverItem)
  768.           gCurrentDragOverItem = toolbar;
  769.       } else
  770.         gCurrentDragOverItem = dropTarget;
  771.     }    
  772.  
  773.     if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
  774.       setDragActive(previousDragItem, false);
  775.     }
  776.     
  777.     setDragActive(gCurrentDragOverItem, true);
  778.     
  779.     aDragSession.canDrop = true;
  780.   },
  781.   
  782.   onDrop: function (aEvent, aXferData, aDragSession)
  783.   {
  784.     if (!gCurrentDragOverItem)
  785.       return;
  786.     
  787.     setDragActive(gCurrentDragOverItem, false);
  788.  
  789.     var draggedItemId = aXferData.data;
  790.     if (gCurrentDragOverItem.id == draggedItemId)
  791.       return;
  792.  
  793.     var toolbar = aEvent.target;
  794.     while (toolbar.localName != "toolbar")
  795.       toolbar = toolbar.parentNode;
  796.  
  797.     var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);       
  798.     if (!draggedPaletteWrapper) {
  799.       // The wrapper has been dragged from the toolbar.
  800.       
  801.       // Get the wrapper from the toolbar document and make sure that
  802.       // it isn't being dropped on itself.
  803.       var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
  804.       if (wrapper == gCurrentDragOverItem)
  805.         return;
  806.  
  807.       // Don't allow static kids (e.g., the menubar) to move.
  808.       if (wrapper.parentNode.firstPermanentChild && wrapper.parentNode.firstPermanentChild.id == wrapper.firstChild.id)
  809.         return;
  810.       if (wrapper.parentNode.lastPermanentChild && wrapper.parentNode.lastPermanentChild.id == wrapper.firstChild.id)
  811.         return;
  812.  
  813.       // Remove the item from it's place in the toolbar.
  814.       wrapper.parentNode.removeChild(wrapper);
  815.  
  816.       // Determine which toolbar we are dropping on.
  817.       var dropToolbar = null;
  818.       if (gCurrentDragOverItem.localName == "toolbar")
  819.         dropToolbar = gCurrentDragOverItem;
  820.       else
  821.         dropToolbar = gCurrentDragOverItem.parentNode;
  822.       
  823.       // Insert the item into the toolbar.
  824.       if (gCurrentDragOverItem != dropToolbar)
  825.         dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
  826.       else
  827.         dropToolbar.appendChild(wrapper);
  828.     } else {
  829.       // The item has been dragged from the palette
  830.       
  831.       // Create a new wrapper for the item. We don't know the id yet.
  832.       var wrapper = createWrapper("");
  833.  
  834.       // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
  835.       var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
  836.       
  837.       // Prepare the item and wrapper to look good on the toolbar.
  838.       cleanupItemForToolbar(newItem, wrapper);
  839.       wrapper.id = "wrapper-"+newItem.id;
  840.       wrapper.flex = newItem.flex;
  841.  
  842.       // Remove the wrapper from the palette.
  843.       var currentRow = draggedPaletteWrapper.parentNode;
  844.       if (draggedItemId != "separator" &&
  845.           draggedItemId != "spring" &&
  846.           draggedItemId != "spacer")
  847.       {
  848.         currentRow.removeChild(draggedPaletteWrapper);
  849.  
  850.         while (currentRow) {
  851.           // Pull the first child of the next row up
  852.           // into this row.
  853.           var nextRow = currentRow.nextSibling;
  854.           
  855.           if (!nextRow) {
  856.             var last = currentRow.lastChild;
  857.             var first = currentRow.firstChild;
  858.             if (first == last) {
  859.               // Kill the row.
  860.               currentRow.parentNode.removeChild(currentRow);
  861.               break;
  862.             }
  863.  
  864.             if (last.localName == "spacer") {
  865.               var flex = last.getAttribute("flex");
  866.               last.setAttribute("flex", ++flex);
  867.               // Reflow doesn't happen for some reason.  Trigger it with a hide/show. ICK! -dwh
  868.               last.hidden = true;
  869.               last.hidden = false;
  870.               break;
  871.             } else {
  872.               // Make a spacer and give it a flex of 1.
  873.               var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  874.                                                     "spacer");
  875.               spacer.setAttribute("flex", "1");
  876.               currentRow.appendChild(spacer);
  877.             }
  878.             break;
  879.           }
  880.           
  881.           currentRow.appendChild(nextRow.firstChild);
  882.           currentRow = currentRow.nextSibling;
  883.         }
  884.       }
  885.     }
  886.     
  887.     gCurrentDragOverItem = null;
  888.  
  889.     repositionDialog();
  890.     gToolboxChanged = true;
  891.   },
  892.   
  893.   _flavourSet: null,
  894.   
  895.   getSupportedFlavours: function ()
  896.   {
  897.     if (!this._flavourSet) {
  898.       this._flavourSet = new FlavourSet();
  899.       var documentId = gToolboxDocument.documentElement.id;
  900.       this._flavourSet.appendFlavour("text/toolbarwrapper-id/"+documentId);
  901.     }
  902.     return this._flavourSet;
  903.   }
  904. }
  905.  
  906. var paletteDNDObserver =
  907. {
  908.   onDragOver: function (aEvent, aFlavour, aDragSession)
  909.   {
  910.     aDragSession.canDrop = true;
  911.   },
  912.   
  913.   onDrop: function(aEvent, aXferData, aDragSession)
  914.   {
  915.     var itemId = aXferData.data;
  916.     
  917.     var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
  918.     if (wrapper) {
  919.       // Don't allow static kids (e.g., the menubar) to move.
  920.       if (wrapper.parentNode.firstPermanentChild && wrapper.parentNode.firstPermanentChild.id == wrapper.firstChild.id)
  921.         return;
  922.       if (wrapper.parentNode.lastPermanentChild && wrapper.parentNode.lastPermanentChild.id == wrapper.firstChild.id)
  923.         return;
  924.  
  925.       // The item was dragged out of the toolbar.
  926.       wrapper.parentNode.removeChild(wrapper);
  927.       
  928.       var wrapperType = wrapper.getAttribute("type");
  929.       if (wrapperType != "separator" && wrapperType != "spacer" && wrapperType != "spring") {
  930.         // Find the template node in the toolbox palette
  931.         var templateNode = gToolbox.palette.firstChild;
  932.         while (templateNode) {
  933.           if (templateNode.id == itemId)
  934.             break;
  935.           templateNode = templateNode.nextSibling;
  936.         }
  937.         if (!templateNode)
  938.           return;
  939.         
  940.         // Clone the template and add it to our palette.
  941.         var paletteItem = templateNode.cloneNode(true);
  942.         appendPaletteItem(paletteItem);
  943.       }
  944.     }
  945.     
  946.     repositionDialog();
  947.     gToolboxChanged = true;
  948.   },
  949.   
  950.   _flavourSet: null,
  951.   
  952.   getSupportedFlavours: function ()
  953.   {
  954.     if (!this._flavourSet) {
  955.       this._flavourSet = new FlavourSet();
  956.       var documentId = gToolboxDocument.documentElement.id;
  957.       this._flavourSet.appendFlavour("text/toolbarwrapper-id/"+documentId);
  958.     }
  959.     return this._flavourSet;
  960.   }
  961. }
  962.  
  963.